0x00 前言
更新完Crypto新手区的题目之后,接下来开始练习进阶区的题目。本系列文章持续更新中~
0x01 告诉你个秘密
解题思路
打开附件发现为两行十六进制数据,一开始考虑将其转换成十进制,后来发现转完之后不易和字符串建立联系,遂考虑直接将其转换为字符串。
这里使用python的binascii库中的binascii.a2b_hex(hex)
实现将十六进制字符串转换成ASCII字符串并输出。输出结果显然为Base64加密,直接解密得到如下结果:
乍看是一堆无意义的字符串,但注意其使用空格作为分隔符分开,考虑到键盘密码,与键盘上的字母对应得到最后的flag。注意本题flag的格式直接为最后得到的字符串,没有任何前缀。
解题脚本
1 | import base64 |
一些经验
下面来讨论一下使用binascii库进行ASCII字符串与十六进制字符串转换的细节。
由上图可知,在ASCII字符串与十六进制字符串转换过程中,binascii库提供了四个函数。
我们可以使用binascii.a2b_hex(hex_string)
和binascii.unhexlify(hex_string)
这两个函数实现从十六进制字符串到ASCII字符串的转换过程。可以使用binascii.b2a_hex(ASCII_string)
和binascii.hexlify(ASCII_string)
这两个函数实现从ASCII字符串到十六进制字符串的转换过程。值得注意的是,在python3中,互相转换的数据类型为byte型而非string型,否则就会出现TypeError报错。
0x02 Broadcast
解题思路
本题目提供了加密脚本task.py
,Bob、Dan、Carol和Erin的pem与enc文件。单独看这道题目确实很简单,因为flag直接包含在题目给出的脚本中。但是如果对题目提供的加密方式进行挖掘,可以得到许多有趣的信息。
题目名字为Broadcast,实际上并不是简单的广播攻击。对于RSA的简单广播攻击,其前提是对同一个m加密,攻击细节如下图:
分析加密脚本中对待加密信息的处理过程:
1 | data = {'from': sha256( b'Alice' ).hexdigest(), |
显然每一次的m不同,并且取e=3
和e=5
时分别只有两个其他明密文对,无法使用应对传统广播攻击的方法进行处理。
观察可知,每次m都由以下内容构成:
- from Alice(每次相同)
- to name(每次不同)
- msg(每次相同)
其中只有to: name
每次变化,而另外两个参数保持不变。由于引入语句data = json.dumps(data, sort_keys=True)
,在加密之前会根据data字典的key进行排序,由于三个键的首字母排序为from-msg-to
,可以发现msg被放到中间位置。即m = high + mid + low
。
对于每次构造的信息m,其高、中位('from':Alice
,'msg': msg
)都是不变的,只是低位由于每次接收方不同有所不同。由于high
和low
已知的(可以通过构造过程中提供的方法进行计算),我们只需要求出mid
部分即可。
由于low
部分包含64位的sha256摘要信息以及12位其他符号,故构造m如下:
其中a = 1, $b{i} = high*2^{1368} + low{i}$
且x仅为95*8=760位
此时可以利用Broad Attack with Linear Padding结合Copper Smith Attack进行求解。
构造多项式$f(x) = (x*2^{608} + b) ^{3} - c (mod n{1}n{2})$的small root
。
需要注意的是,small root
要求小于模数n的1/e次方,而x为760位,7603 = 2280 > 2048 = 10242,所以需要使用两组加密信息使得模数的位数增大为4096位,从而760位的x满足small root
的条件。
解题代码
根据以上分析,根据给出的pem和enc文件提取两对使用3作为加密指数的(n, c)对,编写sage脚本解密如下:
1 | from functools import reduce |
0x03 cr3-what-is-this-encryption
解题思路
该题以十六进制明文的方式给出了p, q, e
这三个RSA解密需要的参数以及密文c
,直接编写脚本解密即可。需要用到gmpy2
和binascii
这两个库。
解题脚本
1 | import gmpy2 |
0x04 flag_in_your_hand1 & flag_in_your_hand
解题思路
这两个题的思路和flag都是一样的(小声逼逼
下载附件并解压,文件夹中有一个html
页面和一个js脚本。首先进入页面,容易得到flag的获取流程为:手动输入Token,并运行getFlag()
函数,步进查看该函数细节。
可以很清楚地看到,该函数首先获取输入框中的Token值,并将其传入checkToken()
函数,返回结果为ic,然后将其传入bm()
函数,该函数返回结果为fg。进入提供的js脚本中查看这两个函数,可以发现bm()
函数与flag的生成过程有关,分析可以得到其运用单表替换的编码方式。单独查看checkToken()
函数可以发现其返回结果为false。但结合bm()
函数的运行流程可以发现,ic的值会通过ck()
函数覆盖。
观察ck()
函数内部可知,其检验过程要求输入Token中每个字母的ASCII值与数组a中的对应位置ASCII值相差3,于是可以编写脚本得到验证正确的Token,输入以获得flag。
解题脚本
1 | password = '' |
0x05 工业协议分析1
解题思路
下载附件后发现其为pacap
文件,记录网络流量。使用wireshark打开后,追踪TCP流,在结果中搜索flag字符串,发现flag.txt
字样,考虑网络流量中包含与flag相关的txt文件。
在命令行中搜索发现flag.txt
结果不存在有意义的明文,遂搜索其他可能发送的文件类型:
1 | grep ".txt" -a sample.pcap |
当搜索png图片相关时,发现网络环境中传送了base64加密的图片。
编写脚本解密即可得到flag。
解题脚本
1 | import base64 |
0x06 你猜猜
解题思路
下载并打开题目给出的附件,发现其开头为50 4B 03 04
显然是zip
格式压缩包的开头,我们将其复制粘贴到Winhex中,注意保存的时候选择ASCII Hex
选项,可以看到其ASCII字符以PK开头并且内部包含flag.txt。
将该压缩包解压发现需要密码,首先考虑zip伪加密发现不符合要求,于是使用密钥爆破软件进行爆破,得到最终密码为123456,解压得flag。
一些经验
先知社区有一篇非常详细的手撕压缩包过程,对压缩包结构的理解有一些帮助:从做CTF题到手撕ZIP。
0x07 fanfie
解题思路
攻防世界上的题目既没有提示也没有flag格式,直接给了一堆看上去像base32编码的字符串,完全没有思路。于是找到原题得到两个重要提示。这样我们有如下题目条件:
1 | 1.flag的格式为:BITSCTF{} |
可以看到题目提供的密文是对base32加密结果进行未知方式加密的结果。所以我们需要将提供的密文先还原成base32加密的结果,再进行base32解密得到最终flag。
base32转换过程中将明文按照每五个字符为一组进行分组,将结果进行base32加密后长度填充为8的倍数。由于已知flag以BITSCTF
开头,我们取前五个字符进行base32加密,得到结果为MZYVMIWL
,看起来该未知加密方式为单表替代加密。构建字母表并寻找对应关系如下:
整理得到如下关系式:
1 | 3->11 |
假设为仿射密码,可以解出a = 13, b = 4, modulus = 27
,编写脚本解密后将结果进行长度填充至8的倍数,再进行一次base32解密即可。原来这个题目的名字进行移位之后会变成affine啊(小声逼逼
解题脚本
1 | import base64 |
0x08 wtc rsa bbq
解题思路
拿到该题目的附件并解压,发现其包括key.pem
和cipher.bin
,说明密钥以证书的形式存在,而加密结果以二进制文件的形式存在。
进入到Kali中使用系统自带OpenSSL的命令:rsa -pubin -text -modulus -in warmup -in key.pem
,获取公钥对如下:
可以发现其模数N非常大,e为标准加密指数。编写脚本将密文以十六进制的形式从cipher.bin
中读出。
1 | ciphertext = open('./cipher.bin', 'rb').read().encode('hex') |
至此,解密数据的提取过程完成。
整道题目没有其他可以操作的参数,我们只能分解模数。观察模数N,发现其位数较多,显然直接分解这条路行不通(指factordb,这里使用yafu的话理论上是可以跑出来的,我们考虑费马分解法。
费马分解法适用于RSA加密过程中选取的大素数p,q较为相近的情况,运用到了|p-q|
这一参数较小的原理进行爆破,从而实现分解模数N的功能。编写费马分解函数将N分解之后即可正常解密得flag。
解题脚本
1 | import math |
一些经验
当题目只给出一个加密过程(广播攻击不适用)且其他参数没有明显可以攻击的特征时,我们只能考虑直接分解模数N。对于模数N的分解,我们常使用两种方法:借助大整数分解网站和使用Windows自带的工具yafu。前者只适合分解一些初级题目中位数较小的模数,后者包含多种模数分解方法,但效率较低。除此之外我们可以参考GitHub上的CtfRSATools这一项目,其中模数分解环节提供了许多常见的分解算法。
所有分解算法中,费马分解法和Pollard p-1分解法。下面简要介绍这两种分解方法并给出分解脚本。
费马分解法
该方法适用于构成模数N的两个大素数p,q相近即|p-q|
较小的情况。过程如下:
任何一个正整数n都能拆成$n = 2^{k} a$的形式,其中a为一个奇数。若$a = cd$,则令$x = (c+d)/2$,$y = (c-d)/2$,则$x^{2} - y^{2} = a$,枚举$x^{2}$观察$x^{2}-a$是否为完全平方数,如果是,则$c = x+(x^{2}-a)^{(1/2)}$和$d = x-(x^{2}-a)^{(1/2)}$就是a的因子。我们通过枚举$x^{2}$可以找到a的所有因子。
代码如下:
1 | import gmpy2 |
Pollard p-1分解法
Pollard p-1方法由Pollard提出,适用于p-1或q-1能够被小素数整除的情况。具体流程如下:
代码如下:
1 | import gmpy2 |
0x09 工业协议分析2
解题思路
该题目给出pcap附件,可以想到为流量分析,使用wireshark打开,发现存在关于ARP、UDP、SNA协议的流量包以及大量的UDP流量包。
观察UDP流量包的长度,可以看到有些长度仅出现一次,其余长度多次出现。于是猜测这些流量包存在异常。分别分析这些流量包,发现长度为147和179的流量包出现异常字符串:
长度为147的流量包传输的数据以flag
结尾。故推测下一个从192.168.1.123
向192.168.1.181
发送的流量包可能包含flag信息。果然从长度为179的数据包中发现可疑数据:
将末尾的十六进制字符串转换为ASCII字符串可得flag。
0x0A 工控安全取证
解题思路
题目给出.log
文件并要求查找发起第四次扫描时的数据包编号。使用wireshark打开,发现有UDP,ICMP和TCP三种类型的数据包,并存在大量的TCP流量。
观察发现,UDP流量只有三条记录,TCP流量记录过多,显然第四次扫描时的数据包不在这两类流量中。接着分析剩下的ICMP数据包,可以看到每次发送大量TCP数据包之前,先发送ICMP数据包。找到第四次建立连接发送的ICMP数据包,其编号为flag。
0x0B 小结
这十道题目里面需要特别注意的是第二题Broadcast和第八题wtc rsa bbq。前者提供了一种与普通RSA广播攻击不同的情形,引入了Hastad Attack以及Broadcast Attack with Linear Padding,并且其结合sagemath编写的解题模板值得借鉴。后者考察了只提供一对(c, n, e)
情况下对参数n的处理,除在线网站及yafu分解外,还可以考虑Fermat Resolve和Pollard p-1 Resolve,注意二者适用的p,q对应情况。
以及,高校战疫的Crypto题目真的好少。pwntools杀我,这辈子都不想再看到EOFError了!